<?php
namespace bdhert\PhpBitfield;

use bdhert\PhpBitfield\exception\InformatsException;
use bdhert\PhpBitfield\exception\StructException;
use bdhert\PhpBitfield\exception\BitFieldException;

/**
 * 位域解析
 * Class BitANLS
 * @package bdhert\PhpBitfield
 */
class BitANLS {
    public string $bitfield = '';

    // 状态值
    protected object $head;
    protected array  $conditions = [];
    protected int    $page       = 0;
    protected int    $limit      = 20;
    protected int    $sort       = SORT_ASC;

    public function __construct(string $binary) {
        $this->initialize($binary);
    }

    /**
     * 位域初始化
     * @param string $binary
     * @return $this
     */
    public function initialize(string $binary) {
        if (empty($binary)) throw new InformatsException('源数据为空', 400);
        $this->bitfield = BitRigger::toBinary($binary);

        // 初始化
        $this->reset()->head();

        // 数据结构检测
        if (!($this instanceof BitString)) throw new BitFieldException('初始化错误', 500);
        if (!$this->formatCheck()) throw new StructException('数据类型错误', 400);

        return $this;
    }

    /**
     * 状态重置
     * @return $this
     */
    protected function reset(bool $join_head = true) {
        [$this->conditions, $this->page, $this->limit, $this->sort] = [[], 0, 20, SORT_ASC];

        $join_head && $this->head = (object)[];

        return $this;
    }

    /**
     * 获取串头信息
     * @return object
     */
    public function head(): object {
        if (is_null($f_len = $this->pick(6, 4))) throw new StructException('头信息错误', 400);

        // 字段信息
        $fields = [];
        for ($i = 0; $i < $f_len; $i++) {
            if (is_null($fields[] = $this->pick(10 + $i * 5, 5)))
                throw new StructException('头信息错误', 400);
        }

        if (is_null($index = $this->pick(0, 6)))
            throw new StructException('头信息错误', 400);

        if (is_null($total = $this->pick(85, 10)))
            throw new StructException('头信息错误', 400);

        if (is_null($bitcount = $this->pick(95, 10)))
            throw new StructException('头信息错误', 400);

        return ($this->head = (object)[
            'index' => $index, 'total' => $total > 1022 ? 40000 : $total, 'bitcount' => $bitcount, 'field' => (object)[
                'total' => $f_len, 'fields' => $fields, 'length' => array_sum($fields)
            ]
        ]);
    }

    /**
     * 输出串
     * @return mixed|string
     */
    public function string(): string {
        return BitRigger::toString($this->bitfield);
    }

    /**
     * 遍历操作
     * @param \Closure $handle
     * @param bool $is_filter
     * @param bool $is_paging
     */
    protected function mapActions(\Closure $handle, bool $is_filter = true, bool $is_paging = false): void {
        if ($this->head->field->total) {
            if ($is_paging && $this->page) {
                $start = ($this->page - 1) * $this->limit;
                $end   = $start + $this->limit;
            }

            $hit_num = 0;
            for ($m = 0; $m < $this->head->total; $m++) {
                $m_index = $m;
                if (SORT_DESC === $this->sort) {
                    $m_index = $this->head->total - $m - 1;
                }

                if (isset($end) && $hit_num >= $end) break;

                // 筛选
                $field = [];
                if ($is_filter && !$this->operator('index', $m_index)) continue;
                for ($n = 0; $n < $this->head->field->total; $n++) {
                    $value = $this->getValue($m_index, $n, !$is_filter);
                    if ($value === false) break 2;
                    if (is_null($value)) continue 2;

                    $field[] = $value;
                }
                $hit_num++;

                if (isset($start) && ($hit_num - 1) < $start) continue;

                $handle($m_index, $field);
            }
        }
    }

    /**
     * 获取段
     * @param int $m
     * @param int $n
     * @param bool $source
     * @return int|null
     */
    protected function getValue(int $m, int $n, bool $source = false) {
        $value = $this->pick($this->offset($m, $n), $this->length($n));
        if (!$source && is_null($value)) return false;

        if ($source || $this->operator($n, $value)) return $value;

        return NULL;
    }

    /**
     * 更新段
     * @param int $m
     * @param int $n
     * @param int $value
     * @param bool $source
     * @return bool
     */
    protected function setValue(int $m, int $n, int $value, bool $source = false) {
        $offset  = $this->offset($m, $n);
        $bit_num = $this->length($n);
        if ($source) return $this->replace($offset, $bit_num, $value);

        $pre_value = $this->pick($offset, $bit_num);
        if ($this->operator($n, $pre_value)) return $this->replace($offset, $bit_num, $value);

        return false;
    }

    /**
     * 添加段
     * @param array $fields
     * @return string
     */
    protected function addValue(array $fields) {
        $save = '';
        foreach ($this->head->field->fields as $field_index => $field_len) {
            BitRigger::push($save, $fields[$field_index] ?? 0, $field_len);
        }

        $start = 105 + $this->head->total * $this->head->field->length;
        $this->bitfield = substr_replace($this->bitfield, $save, $start, $this->head->field->length);

        try {
            $this->replace(85, 10, ++$this->head->total);
        } catch (StructException $error) {
            // todo 这里有设计失误，把统计上限设置小了，原本考虑一个字段不应该过长，但实际应用中多片不实用
        }

        if ($this->isBitmap() && ($fields[0] ?? 0)) $this->replace(95, 10, ++$this->head->bitcount);

        return $save;
    }

    /**
     * 条件比较
     * @param $filter_index
     * @param $champion
     * @return bool
     */
    protected function operator($filter_index, $champion) {
        is_numeric($filter_index) && $filter_index = (int)$filter_index;
        if (empty($filters = $this->conditions[$filter_index] ?? NULL)) return true;

        foreach ($filters as $filter) {
            if (!Compare::operator($champion, $filter[0], $filter[1])) return false;
        }

        return true;
    }

    /**
     * 获取段始位置
     * @param int $m
     * @param int $n
     * @return float|int|null
     */
    protected function offset(int $m, int $n) {
        $field_len = 0;
        for ($i = 0; $i < $n; $i++) {
            $field_len += $this->length($i);
        }

        return 105 + $m * $this->head->field->length + $field_len;
    }

    /**
     * 获取段长
     * @param int $n
     * @return |null
     */
    protected function length(int $n) {
        if (is_null($field_num = $this->head->field->fields[$n] ?? NULL))
            throw new StructException("字段索引范围错误 index:{$n}", 400);

        return $field_num;
    }

    /**
     * 摘取
     * @param int $start
     * @param int $length
     * @return int|null
     */
    protected function pick(int $start, int $length) {
        return BitRigger::pick($start, $length, $this->bitfield);
    }

    /**
     * 替换
     * @param int $start
     * @param int $length
     * @param int $value
     * @return bool
     */
    protected function replace(int $start, int $length, int $value) {
        return BitRigger::replace($start, $length, $this->bitfield, $value);
    }

    /**
     * 判断是否包含位图结构
     * @param int $field_index
     * @return bool
     */
    protected function isBitmap(int $field_index = 0) {
        return 1 === ($this->head->field->fields[0] ?? 0) && !$field_index;
    }

    /**
     * 是否是二维数组
     * @param array $dimensions
     * @return bool
     */
    protected function isDimensions(array $dimensions) {
        foreach ($dimensions as $dimension) {
            if(!is_array($dimension)) return false;
        }

        return true;
    }
}